// threadtest.cc 
//	Simple test case for the threads assignment.
//
//	Create two threads, and have them context switch
//	back and forth between themselves by calling Thread::Yield, 
//	to illustratethe inner workings of the thread system.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.


#include "copyright.h"
#include "system.h"

#ifdef CHANGED
#include "synch.h"
#include "p2head.h"
#endif

#ifdef CHANGED
// --------------------------------------------------
// Test Suite
// --------------------------------------------------


// --------------------------------------------------
// Test 1 - see TestSuite() for details
// --------------------------------------------------
Semaphore t1_s1("t1_s1",0);       // To make sure t1_t1 acquires the
                                  // lock before t1_t2
Semaphore t1_s2("t1_s2",0);       // To make sure t1_t2 Is waiting on the 
                                  // lock before t1_t3 releases it
Semaphore t1_s3("t1_s3",0);       // To make sure t1_t1 does not release the
                                  // lock before t1_t3 tries to acquire it
Semaphore t1_done("t1_done",0);   // So that TestSuite knows when Test 1 is
                                  // done
Lock t1_l1("t1_l1");		  // the lock tested in Test 1

// --------------------------------------------------
// t1_t1() -- test1 thread 1
//     This is the rightful lock owner
// --------------------------------------------------
void t1_t1() {
    t1_l1.Acquire();
    t1_s1.V();  // Allow t1_t2 to try to Acquire Lock
 
    printf ("%s: Acquired Lock %s, waiting for t3\n",currentThread->getName(),
	    t1_l1.getName());
    t1_s3.P();
    printf ("%s: working in CS\n",currentThread->getName());
    for (int i = 0; i < 1000000; i++) ;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t2() -- test1 thread 2
//     This thread will wait on the held lock.
// --------------------------------------------------
void t1_t2() {

    t1_s1.P();	// Wait until t1 has the lock
    t1_s2.V();  // Let t3 try to acquire the lock

    printf("%s: trying to acquire lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Acquire();

    printf ("%s: Acquired Lock %s, working in CS\n",currentThread->getName(),
	    t1_l1.getName());
    for (int i = 0; i < 10; i++)
	;
    printf ("%s: Releasing Lock %s\n",currentThread->getName(),
	    t1_l1.getName());
    t1_l1.Release();
    t1_done.V();
}

// --------------------------------------------------
// t1_t3() -- test1 thread 3
//     This thread will try to release the lock illegally
// --------------------------------------------------
void t1_t3() {

    t1_s2.P();	// Wait until t2 is ready to try to acquire the lock

    t1_s3.V();	// Let t1 do it's stuff
    for ( int i = 0; i < 3; i++ ) {
	printf("%s: Trying to release Lock %s\n",currentThread->getName(),
	       t1_l1.getName());
	t1_l1.Release();
    }
}

// --------------------------------------------------
// Test 2 - see TestSuite() for details
// --------------------------------------------------
Lock t2_l1("t2_l1");		// For mutual exclusion
Condition t2_c1("t2_c1");	// The condition variable to test
Semaphore t2_s1("t2_s1",0);	// To ensure the Signal comes before the wait
Semaphore t2_done("t2_done",0);     // So that TestSuite knows when Test 2 is
                                  // done

// --------------------------------------------------
// t2_t1() -- test 2 thread 1
//     This thread will signal a variable with nothing waiting
// --------------------------------------------------
void t2_t1() {
    t2_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
	   t2_l1.getName(), t2_c1.getName());
    t2_c1.Signal(&t2_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t2_l1.getName());
    t2_l1.Release();
    t2_s1.V();	// release t2_t2
    t2_done.V();
}

// --------------------------------------------------
// t2_t2() -- test 2 thread 2
//     This thread will wait on a pre-signalled variable
// --------------------------------------------------
void t2_t2() {
    t2_s1.P();	// Wait for t2_t1 to be done with the lock
    t2_l1.Acquire();
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t2_l1.getName(), t2_c1.getName());
    t2_c1.Wait(&t2_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t2_l1.getName());
    t2_l1.Release();
}
// --------------------------------------------------
// Test 3 - see TestSuite() for details
// --------------------------------------------------
Lock t3_l1("t3_l1");		// For mutual exclusion
Condition t3_c1("t3_c1");	// The condition variable to test
Semaphore t3_s1("t3_s1",0);	// To ensure the Signal comes before the wait
Semaphore t3_done("t3_done",0); // So that TestSuite knows when Test 3 is
                                // done

// --------------------------------------------------
// t3_waiter()
//     These threads will wait on the t3_c1 condition variable.  Only
//     one t3_waiter will be released
// --------------------------------------------------
void t3_waiter() {
    t3_l1.Acquire();
    t3_s1.V();		// Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t3_l1.getName(), t3_c1.getName());
    t3_c1.Wait(&t3_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t3_c1.getName());
    t3_l1.Release();
    t3_done.V();
}


// --------------------------------------------------
// t3_signaller()
//     This threads will signal the t3_c1 condition variable.  Only
//     one t3_signaller will be released
// --------------------------------------------------
void t3_signaller() {

    // Don't signal until someone's waiting
    
    for ( int i = 0; i < 5 ; i++ ) 
	t3_s1.P();
    t3_l1.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
	   t3_l1.getName(), t3_c1.getName());
    t3_c1.Signal(&t3_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t3_l1.getName());	
    t3_l1.Release();
    t3_done.V();
}
 
// --------------------------------------------------
// Test 4 - see TestSuite() for details
// --------------------------------------------------
Lock t4_l1("t4_l1");		// For mutual exclusion
Condition t4_c1("t4_c1");	// The condition variable to test
Semaphore t4_s1("t4_s1",0);	// To ensure the Signal comes before the wait
Semaphore t4_done("t4_done",0); // So that TestSuite knows when Test 4 is
                                // done

// --------------------------------------------------
// t4_waiter()
//     These threads will wait on the t4_c1 condition variable.  All
//     t4_waiters will be released
// --------------------------------------------------
void t4_waiter() {
    t4_l1.Acquire();
    t4_s1.V();		// Let the signaller know we're ready to wait
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t4_l1.getName(), t4_c1.getName());
    t4_c1.Wait(&t4_l1);
    printf("%s: freed from %s\n",currentThread->getName(), t4_c1.getName());
    t4_l1.Release();
    t4_done.V();
}


// --------------------------------------------------
// t2_signaller()
//     This thread will broadcast to the t4_c1 condition variable.
//     All t4_waiters will be released
// --------------------------------------------------
void t4_signaller() {

    // Don't broadcast until someone's waiting
    
    for ( int i = 0; i < 5 ; i++ ) 
	t4_s1.P();
    t4_l1.Acquire();
    printf("%s: Lock %s acquired, broadcasting %s\n",currentThread->getName(),
	   t4_l1.getName(), t4_c1.getName());
    t4_c1.Broadcast(&t4_l1);
    printf("%s: Releasing %s\n",currentThread->getName(), t4_l1.getName());
    t4_l1.Release();
    t4_done.V();
}
// --------------------------------------------------
// Test 5 - see TestSuite() for details
// --------------------------------------------------
Lock t5_l1("t5_l1");		// For mutual exclusion
Lock t5_l2("t5_l2");		// Second lock for the bad behavior
Condition t5_c1("t5_c1");	// The condition variable to test
Semaphore t5_s1("t5_s1",0);	// To make sure t5_t2 acquires the lock after
                                // t5_t1

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a condition under t5_l1
// --------------------------------------------------
void t5_t1() {
    t5_l1.Acquire();
    t5_s1.V();	// release t5_t2
    printf("%s: Lock %s acquired, waiting on %s\n",currentThread->getName(),
	   t5_l1.getName(), t5_c1.getName());
    t5_c1.Wait(&t5_l1);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// t5_t1() -- test 5 thread 1
//     This thread will wait on a t5_c1 condition under t5_l2, which is
//     a Fatal error
// --------------------------------------------------
void t5_t2() {
    t5_s1.P();	// Wait for t5_t1 to get into the monitor
    t5_l1.Acquire();
    t5_l2.Acquire();
    printf("%s: Lock %s acquired, signalling %s\n",currentThread->getName(),
	   t5_l2.getName(), t5_c1.getName());
    t5_c1.Signal(&t5_l2);
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l2.getName());
    t5_l2.Release();
    printf("%s: Releasing Lock %s\n",currentThread->getName(),
	   t5_l1.getName());
    t5_l1.Release();
}

// --------------------------------------------------
// TestSuite()
//     This is the main thread of the test suite.  It runs the
//     following tests:
//
//       1.  Show that a thread trying to release a lock it does not
//       hold does not work
//
//       2.  Show that Signals are not stored -- a Signal with no
//       thread waiting is ignored
//
//       3.  Show that Signal only wakes 1 thread
//
//	 4.  Show that Broadcast wakes all waiting threads
//
//       5.  Show that Signalling a thread waiting under one lock
//       while holding another is a Fatal error
//
//     Fatal errors terminate the thread in question.
// --------------------------------------------------
void TestSuite() {
    Thread *t;
    char *name;
    int i;
    
    // Test 1

    printf("Starting Test 1\n");

    t = new Thread("t1_t1");
    t->Fork((VoidFunctionPtr)t1_t1,0);

    t = new Thread("t1_t2");
    t->Fork((VoidFunctionPtr)t1_t2,0);

    t = new Thread("t1_t3");
    t->Fork((VoidFunctionPtr)t1_t3,0);

    // Wait for Test 1 to complete
    for (  i = 0; i < 2; i++ )
	t1_done.P();

    // Test 2

    printf("Starting Test 2.  Note that it is an error if thread t2_t2\n");
    printf("completes\n");

    t = new Thread("t2_t1");
    t->Fork((VoidFunctionPtr)t2_t1,0);

    t = new Thread("t2_t2");
    t->Fork((VoidFunctionPtr)t2_t2,0);

    // Wait for Test 2 to complete
    t2_done.P();

    // Test 3

    printf("Starting Test 3\n");

    for (  i = 0 ; i < 5 ; i++ ) {
	name = new char [20];
	sprintf(name,"t3_waiter%d",i);
	t = new Thread(name);
	t->Fork((VoidFunctionPtr)t3_waiter,0);
    }
    t = new Thread("t3_signaller");
    t->Fork((VoidFunctionPtr)t3_signaller,0);

    // Wait for Test 3 to complete
    for (  i = 0; i < 2; i++ )
	t3_done.P();

    // Test 4

    printf("Starting Test 4\n");

    for (  i = 0 ; i < 5 ; i++ ) {
	name = new char [20];
	sprintf(name,"t4_waiter%d",i);
	t = new Thread(name);
	t->Fork((VoidFunctionPtr)t4_waiter,0);
    }
    t = new Thread("t4_signaller");
    t->Fork((VoidFunctionPtr)t4_signaller,0);

    // Wait for Test 4 to complete
    for (  i = 0; i < 6; i++ )
	t4_done.P();

    // Test 5

    printf("Starting Test 5.  Note that it is an error if thread t5_t1\n");
    printf("completes\n");

    t = new Thread("t5_t1");
    t->Fork((VoidFunctionPtr)t5_t1,0);

    t = new Thread("t5_t2");
    t->Fork((VoidFunctionPtr)t5_t2,0);

}
#endif

#ifdef CHANGED
//globle locks and conditions
Lock waitingRoomLock("WR_L");
Lock wrnLock("WRN_L");
Lock nLock("nurse_L");		//Lock for nurse and patient
Lock nwLock("nurseWithWrn");	//lock for nurse and WRN interaction
Lock nwwLock("NWW"); //lock for control waiting count of nurse
Condition waitingRoomCondition("WR_C"); //patient wait for WRN
Condition wrnWaiting("WRN_C");	//WRN wait for patient
Condition nurseWaiting("nurseWaiting");	//patient wait for nurse
Condition nwCondition("NW_C");	//CV for nurse and WRN
Condition nwwCondition("NWW_C"); //CV for count of nurse
Lock exRoomLock("ex_l");
Condition exRoomCondition("ex_c");
Lock room_1_L("RL1");
Lock room_2_L("RL2");
Lock room_3_L("RL3");
Lock room_4_L("RL4");
Lock room_5_L("RL5");
Condition room_1_c("RC1");
Condition room_2_c("RC2");
Condition room_3_c("RC3");
Condition room_4_c("RC4");
Condition room_5_c("RC5");
Lock* roomLock[6];
Condition* roomCondition[6];

//Lock doctorLock("DL");
//Condition doctorCondition("DC");
Lock nurseLock("NL");
Condition nurseCondition("NC");
waitingRoomMoitor WRM;
examinationRoomMoitor erm;
doctorOfficeMonitor dom,dom1,dom2,dom3;

Lock doctor_0_L("DL0");
Lock doctor_1_L("DL1");
Lock doctor_2_L("DL2");
Lock doctor_3_L("DL3");
Condition doctor_0_C("DC0");
Condition doctor_1_C("DC1");
Condition doctor_2_C("DC2");
Condition doctor_3_C("DC3");

Lock* doctorLock[4];
Condition* doctorCondition[4];
doctorOfficeMonitor* doctoroffice[4];

struct examSheet* wallpoket[7];
Lock wallpoketLock("WPL");
int test=0;	//test how many people leave the doctor office

//variables for xray room
List* xrayInQueue = new List;	// sheetqueue wait for the xray(wallpocketIn)
List* xrayOutQueue = new List;	// sheetqueue of done 
int xrayInCount=0;	//number of patient who is waiting for the xray
int xrayOutCount=0;	//number of patient who has finished the xray

//Lock and CVs for xray room
Lock xray_0_L("XL0");
Lock xray_1_L("XL1");
Lock xray_2_L("XL2");
Condition xray_0_C("XC0");
Condition xray_1_C("XC1");
Condition xray_2_C("XC2");
Lock* xrayLock[3];
Condition* xrayCondition[3];
int xrayroom[3];
Lock xraycheck("xc");
//nurse gives sheet to xraytechnician
struct examSheet* xraysheet[3];	

//for interaction of nurse and patient in the xray waiting room
Lock xrayOutLock("XOL");		
Condition xrayOutC("XOC");	
Lock xrayInLock("XIL");
Condition xrayInC("XIC");

//for interation between cashier and adult patient/parent at the cashier counter
cashierCounterMonitor CCM;//for adult patient/parent waiting for cashier
cashierPatientMonitor CPM;//to guarantee that Cashiers only talk to one Patient/Parent at a time.


int doctorNumber;
int technicianNumber;
int parentchildNumber;
int adultpatientNumber;
Lock** pcLock;
Condition** pcConditions;
int* pcstatus;
int* pcdocid;
bool* patientmv;
bool* parentmv;
int testcontrol;
bool nursemv[6];
bool doctormv[6];
bool xraymv[3];
bool wrncv;
Lock finishlock("finish");

Lock cabinetLock("cbl");

void p2test(int DocNum,int NurNum,int XrayNum,int ApNum, int PcNum, int testC){
	
	Thread *t;
	int i;
	Lock* locktemp;
	Condition* conditiontemp;
	
	pcLock = new Lock*[PcNum+ApNum+1];
	for(i=1;i<ApNum+PcNum+1;i++){
		locktemp = new Lock("pclock");
		pcLock[i]=locktemp;
	}
	pcConditions = new Condition*[ApNum+PcNum+1];
	for(i=1;i<ApNum+PcNum+1;i++){
		conditiontemp = new Condition("pccondition");
		pcConditions[i]=conditiontemp;
	}
	pcstatus = new int[ApNum+PcNum];
	pcdocid = new int[ApNum+PcNum];
	patientmv = new bool[ApNum+PcNum];
	
	wrncv=false;
	for(i=0;i<6;i++){
		nursemv[i]=false;
	}
	
	for(i=0;i<6;i++){
		doctormv[i]=false;
	}
	
	for(i=0;i<ApNum+PcNum;i++){
		patientmv[i]=false;
	}
	for(i=0;i<3;i++){
		xraymv[i]=false;
	}
	roomLock[1]=&room_1_L;
	roomLock[2]=&room_2_L;
	roomLock[3]=&room_3_L;
	roomLock[4]=&room_4_L;
	roomLock[5]=&room_5_L;
	roomCondition[1]=&room_1_c;
	roomCondition[2]=&room_2_c;
	roomCondition[3]=&room_3_c;
	roomCondition[4]=&room_4_c;
	roomCondition[5]=&room_5_c;
	doctorLock[0]=&doctor_0_L;
	doctorLock[1]=&doctor_1_L;
	doctorLock[2]=&doctor_2_L;
	doctorLock[3]=&doctor_3_L;
	doctorCondition[0]=&doctor_0_C;
	doctorCondition[1]=&doctor_1_C;
	doctorCondition[2]=&doctor_2_C;
	doctorCondition[3]=&doctor_3_C;
	doctoroffice[0]=&dom;
	doctoroffice[1]=&dom1;
	doctoroffice[2]=&dom2;
	doctoroffice[3]=&dom3;
	xrayLock[0]=&xray_0_L;
	xrayLock[1]=&xray_1_L;
	xrayLock[2]=&xray_2_L;
	xrayCondition[0]=&xray_0_C;
	xrayCondition[1]=&xray_1_C;
	xrayCondition[2]=&xray_2_C;

	doctorNumber=DocNum;
	technicianNumber=XrayNum;
	testcontrol=testC;
	parentchildNumber=PcNum;
	adultpatientNumber=ApNum;
	
	system("clear");
	printf("Number of Doctors (2 to 3): = [%d]\n", DocNum);
	printf("Number of Nurses (2 - 5) = [%d]\n", NurNum);
	printf("Number of XRay Technicians/Rooms (1 - 2) = [%d]\n", XrayNum);
	printf("Number of Patients = [%d]\n", ApNum);
	printf("Number of Parents/Child Patients (30 or more) = [%d]\n", PcNum);
	printf("Number of Examination Rooms (2 - 5) = [5]\n");

	printf("Press enter to continue.");
	getchar();
	
	t= new Thread("WRN");
	t->Fork((VoidFunctionPtr)waitingRoomNurse,1);		

	for(i=1;i<NurNum+1;i++){
	t= new Thread("Nurse");
	t->Fork((VoidFunctionPtr)nurse, i);
	}

	for(i=1;i<DocNum+1;i++){
	t= new Thread("Doctor");
	t->Fork((VoidFunctionPtr)doctor,i);
	}

	for(i=1;i<XrayNum+1;i++){
	t = new Thread("XrayTechnician");
	t->Fork((VoidFunctionPtr)XrayTechnician,i);
	}

	t = new Thread("Cashier");
	t->Fork((VoidFunctionPtr)cashier,1);
	
	for(i=1;i<ApNum+1;i++){		
		t= new Thread("AdultPatient");
		t->Fork((VoidFunctionPtr)patient,i);
	}

	for(i=ApNum+1;i<ApNum+PcNum+1;i++){
		t= new Thread("Parent");
		t->Fork((VoidFunctionPtr)parent,i);
		t= new Thread("Child");
		t->Fork((VoidFunctionPtr)childPatient,i);
	}
	
}

#endif
//----------------------------------------------------------------------
// SimpleThread
// 	Loop 5 times, yielding the CPU to another ready thread 
//	each iteration.
//
//	"which" is simply a number identifying the thread, for debugging
//	purposes.
//----------------------------------------------------------------------

void
SimpleThread(int which)
{
    int num;
    
    for (num = 0; num < 5; num++) {
	printf("*** thread %d looped %d times\n", which, num);
        currentThread->Yield();
    }
}

//----------------------------------------------------------------------
// ThreadTest
// 	Set up a ping-pong between two threads, by forking a thread 
//	to call SimpleThread, and then calling SimpleThread ourselves.
//----------------------------------------------------------------------

void
ThreadTest()
{
    DEBUG('t', "Entering SimpleTest");

    Thread *t = new Thread("forked thread");

    t->Fork(SimpleThread, 1);
    SimpleThread(0);
}

